package cs2340.quarterblack.wheresmystuff;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import cs2340.quarterblack.wheresmystuff.Item.Itemtype;
import cs2340.quarterblack.wheresmystuff.Storage.loginResponse;

public class Account implements Serializable{

	private static final long serialVersionUID = 1L;
	public enum Statustype {unlocked, locked};
	private String userId;
	private String pw;
	private boolean admin;
	private String email;
	private Statustype status;
	public static enum SearchType {name,category,date,status}
	
	/**
	 * this method initializes account's instance variables.
	 * @param userId The ID string that is used to identify the user
	 * @param pw String the password for the user
	 */
	public Account(String userId, String pw)
	{
		this.userId = userId;
		this.pw = pw;
		this.status = Account.Statustype.unlocked;
		admin =false;
	}
	/**
	 * this method initializes account's instance variables
	 * @param userId The ID string that is used to identify the user
	 * @param pw String the password for the user
	 * @param admin whether or not the user is an administrator
	 */
	public Account(String userId,String pw,boolean admin)
	{
		this.userId = userId;
		this.pw = pw;
		this.admin = admin;
		this.status = Account.Statustype.unlocked;
	}
	/**
	 * Checks to see if the given account is a valid account
	 * @return true if the login information was correct. False otherwise
	 */
	public loginResponse checkLogin() 
	{
		Storage storage = StorageManager.getInstance();
		return storage.checkLogin(this);
	}
	/**
	 * locks this account
	 * @return whether the account was successfully locked or not
	 */
	public boolean lockAccount() {
		Storage storage = StorageManager.getInstance();
		return storage.lockAccount(this);
	}
	/**
	 * returns the list of items the user has found
	 * @return list found items under this account
	 */
	public List<Item> getFoundItems() {
		Storage storage = StorageManager.getInstance();
		return storage.getUsersItems(this, Itemtype.FOUND);
	}
	/**
	 * returns a list of items containing the specified string
	 * @param str the string to be searched for
	 * @return list of found items  containing the string
	 */
	public List<Item> getFoundItemsName(String str){
		List<Item> item = getFoundItems();
		str = str.toUpperCase();
		List<Item> ans = new LinkedList<Item>();
		if(item != null)
		{
			for(Item i:item)
			{
				if(i != null)
				{
					if(i.getName().toUpperCase().contains(str))
					{
						ans.add(i);
					}
				}	
			}
		}
		return ans;
	}
	/**
	 * returns a list of items containing the specified string
	 * @param str the string to be searched for
	 * @return list of found items  containing the string
	 */
	public List<Item> getLostItemsDate(){
		List<Item> item = getLostItems();
		List<Item> ans = new LinkedList<Item>();
		if(item != null)
		{
			int p = 0;
			Item[] temp = new Item[item.size()];
			for(Item i:item)
			{
				if(i != null)
				{
					temp[p] = i;
					if(p!= 0)
					{
						for(int b = p;b>0;b--)
						{
							if(i.getDate().before(temp[b].getDate()))
							{
								Item d = temp[b];
								temp[b] = temp[b-1];
								temp[b-1] = d;
							}
						}
					}
				}	
				p++;
			}
			for(int i = 0;i<temp.length;i++)
			{
				ans.add(temp[i]);
			}
		}
		return ans;
	}
	/**
	 * returns a list of items after the specified date
	 * @param Calendar date to be sorted
	 * @return list of lost items after the date
	 */
	public List<Item> getLostItemsDate(Calendar date){
		List<Item> item = getLostItems();
		List<Item> ans = new LinkedList<Item>();
		
		if(item != null){
			for(Item i : item){
				if(i != null){
					//when date of register is after the search date
					if(i.getDate().compareTo(date.getTime())>=0){
						ans.add(i);
					}
				}
			}
		}
		return ans;
	}
	
	
	/**
	 * returns a list of items containing the specified string
	 * @param str the string to be searched for
	 * @return list of found items  containing the string
	 */
	public List<Item> getFoundItemsDate(){
		List<Item> item = getFoundItems();
		List<Item> ans = new LinkedList<Item>();
		if(item != null)
		{
			int p = 0;
			Item[] temp = new Item[item.size()];
			for(Item i:item)
			{
				if(i != null)
				{
					temp[p] = i;
					if(p!= 0)
					{
						for(int b = p;b>0;b--)
						{
							if(i.getDate().before(temp[b].getDate()))
							{
								Item d = temp[b];
								temp[b] = temp[b-1];
								temp[b-1] = d;
							}
						}
					}
				}	
				p++;
			}
			for(int i = 0;i<temp.length;i++)
			{
				ans.add(temp[i]);
			}
		}
		return ans;
	}
	
	/**
	 * returns a list of items after the specified date
	 * @param Calendar date to be sorted
	 * @return list of found items after the date
	 */
	public List<Item> getFoundItemsDate(Calendar date){
		List<Item> item = getFoundItems();		

		return filterItems(item,date,Account.SearchType.date);
	}
	
	
	/**
	 * gets the list of lost items that contain the input string
	 * @param str the string that must be present in the item name
	 * @return the list of string
	 */
	public List<Item> getLostItemsName(String str){
		List<Item> item = getLostItems();
		str = str.toUpperCase();
		
		return filterItems(item,str,Account.SearchType.name);
	}
	/**
	 * filters through item list based on search type
	 * @param item the list of items to be searched
	 * @param str the string to be used to search through the items
	 * @param type the type of search to be done. (by name, category, or date)
	 * @return a list of the sorted items
	 */
	public static List<Item> filterItems(List<Item> item,Object input,SearchType type)
	{
		List<Item>ans = new LinkedList<Item>();
		if(type == SearchType.name)
		{
			String str = (String)input;
			str = str.toUpperCase();
			if(item != null)
			{
				for(Item i:item)
				{
					if(i != null)
					{
						if(i.getName().toUpperCase().contains(str))
						{
							ans.add(i);
						}
					}	
				}
			}
		}
		else if(type == SearchType.category)
		{
			String str = (String)input;
			str = str.toUpperCase();
			if(item != null)
			{
				for(Item i:item)
				{
					if(i != null)
					{
						if(i.getStringCategory().toUpperCase().contains(str))
						{
							ans.add(i);
						}
					}	
				}
			}
		}
		else if(type == SearchType.date)
		{
			Date date = (Date)input;
			if(item != null){
				for(Item i : item){
					if(i != null){
						//when date of register is after the search date
						if(i.getDate().after(date)==true){
							ans.add(i);
						}
						else if(i.getDate().compareTo(date)>=0)
						{
							ans.add(i);
						}
					}
				}
			}
		}
		
		return ans;
	}
	//http://bit.ly/10HG18a
	/**
	 * gets the list of lost items that contain the input string in the category name
	 * @param str the string that must be present in the category name
	 * @return the list of string
	 */
	public List<Item> getLostItemsCategory(String str){
		List<Item> item = getLostItems();
		str = str.toUpperCase();
		return filterItems(item,str,Account.SearchType.category);
	}
	
	
	/**
	 * gets the list of lost items in categorized order
	 * @return the list of categorized items
	 */
	public List<Item> getLostItemsCategory(){
		List<Item> item = getLostItems();
		List<Item> ans = new LinkedList<Item>();
		if(item != null)
		{
			for(int p = 0; p < 4;p++)
			{
				for(Item i:item)
				{
					if(i != null)
					{
						if(p == 0)
						{
							if(i.getCategory() == Item.Category.CLOTHING)
							{
								ans.add(i);
							}
						}
						else if(p == 1)
						{
							if(i.getCategory() == Item.Category.FURNATURE)
							{
								ans.add(i);
							}
						}
						else if(p == 2)
						{
							if(i.getCategory() == Item.Category.PERSONAL)
							{
								ans.add(i);
							}
						}
						else if(p == 3)
						{
							if(i.getCategory() == Item.Category.OTHER)
							{
								ans.add(i);
							}
						}
					}	
				}
			}
		}
		return ans;
	}
	
	
	/**
	 * gets the list of lost items that contain the input string in the category name
	 * @param str the string that must be present in the category name
	 * @return the list of string
	 */
	public List<Item> getFoundItemsCategory(String str){
		List<Item> item = getFoundItems();
		str = str.toUpperCase();
		return filterItems(item,str,Account.SearchType.category);
	}
	
	
	/**
	 * gets the list of lost items in categorized order
	 * @return the list of categorized items
	 */
	public List<Item> getFoundItemsCategory(){
		List<Item> item = getFoundItems();
		List<Item> ans = new LinkedList<Item>();
		if(item != null)
		{
			for(int p = 0; p < 4;p++)
			{
				for(Item i:item)
				{
					if(i != null)
					{
						if(p == 0)
						{
							if(i.getCategory() == Item.Category.CLOTHING)
							{
								ans.add(i);
							}
						}
						else if(p == 1)
						{
							if(i.getCategory() == Item.Category.FURNATURE)
							{
								ans.add(i);
							}
						}
						else if(p == 2)
						{
							if(i.getCategory() == Item.Category.PERSONAL)
							{
								ans.add(i);
							}
						}
						else if(p == 3)
						{
							if(i.getCategory() == Item.Category.OTHER)
							{
								ans.add(i);
							}
						}
					}	
				}
			}
		}
		return ans;
	}
	/**
	 * gets the real version of this account from the database
	 * @return database's version of this account
	 */
	public Account getAccount()
	{
		Storage storage = StorageManager.getInstance();
		return storage.getAccount(this);
	}
	/**
	 * gets a list of the user's lost items
	 * @return the list of items the user has lost
	 */
	public List<Item> getLostItems() {
		Storage storage = StorageManager.getInstance();
		return storage.getUsersItems(this, Itemtype.LOST);
	}
	/**
	 * adds this user to the database
	 * @return whether the add was successful
	 */
	public boolean addUser() {	
		Storage storage = StorageManager.getInstance();
		return storage.addAccount(this);
	}
	/**
	 * sets the email
	 * @param email the email of the user
	 */
	public void addEmail(String email){
		this.email = email;
	}
	/**
	 * deletes a given user from the database storage
	 * @param id the id of the user to be deleted
	 * @return true if the user was deleted. False otherwise.
	 */
	public boolean deleteAccount(String id)
	{
		if(admin == true)
		{
			Storage storage = StorageManager.getInstance();
			return storage.deleteAccount(id);
		}
		else
		{
			return false;
		}
	}
	/**
	 * sets the privalages of the given account to either admin or not
	 * @param admin the value to be given to the admin variable of the specified account
	 * @return whether or not the privelages where successfully changed
	 */
	public boolean changePrivelages(String id,boolean admin)
	{
		if(this.admin == true)
		{
			Storage storage = StorageManager.getInstance();
			return storage.setPrivelages(id,admin);
		}
		else
		{
			return false;
		}
	}
	/**
	 * returns the list of ways that items can be sorted
	 * @return list of ways items can be sorted
	 */
	public static ArrayList<String>getSortList()
	{
		ArrayList<String>str = new ArrayList<String>();
		str.add("name");
		str.add("category");
		str.add("date");
		return str;
	}
	/**
	 * locks the user account specified by the id.  Admin only
	 * @param id the user id of the account to be locked
	 * @return whether the account was locked or not.
	 */
	public boolean lockAccount(String id)
	{
		if(admin == true)
		{
			Storage storage = StorageManager.getInstance();
			return storage.lockAccount(new Account(id,null));
		}
		return false;
	}
	/**
	 * unlocks the user account specified by the id. Admin only
	 * @param id the id of the user to be unlocked
	 * @return whether the account was unlocked or not
	 */
	public boolean unlockAccount(String id)
	{
		if(admin == true)
		{
			Storage storage = StorageManager.getInstance();
			return storage.unlockAccount(new Account(id,null));
		}
		return false;
	}
	/**
	 * checks to see if the indicated account id is a valid account id
	 * @param id the id to be examined.
	 * @return whether the id is valid or not.
	 */
	public boolean checkExists(String id)
	{
		Storage storage = StorageManager.getInstance();
		return storage.checkExists(id);
	}
	/**
	 * returns the user Id
	 * @return userId
	 */
	public String getUserId() {
		return userId;
	}

	/**
	 * sets the user Id to be used at login
	 * @param userId what to set the userID to
	 */
	public void setUserId(String userId) {
		this.userId = userId;
	}
	
	/**
	 * gets the users password
	 * @return password
	 */
	public String getPw() {
		return pw;
	}

	/**
	 * sets the password the user's password should be set to
	 * @param pw what the pasword should be set to
	 */
	public void setPw(String pw) {
		this.pw = pw;
	}

	/**
	 * checks to see whether the user is or is not an admin
	 * @return whether the user is an admin or not
	 */
	public String getAdmin() {
		if (admin) {
			return "true";
		}
		else {
			return "false";
		}
	}
	
	/**
	 * sets the admin quality of the user
	 * @param type what they "type" should be set to
	 */
	public void setAdmin(boolean admin) {
		this.admin = admin;
	}

	/**
	 * gets the user's email
	 * @return email
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * sets the user's email
	 * @param email what the email address should be set to
	 */
	public void setEmail(String email) {
		this.email = email;
	}

	/**
	 * gets the status of the user account. Locked or unlocked
	 * @return status
	 */
	public String getStatus() {
		switch (status) {
			case unlocked: 
				return "unlocked";
			case locked:
				return "locked";
			default:
				return "unknown";
		}
	}

	/**
	 * sets the status of the user account. Locked or unlocked
	 * @param status
	 */
	public void setStatus(Statustype status) {
		this.status = status;
	}
	
	
	
}